// ----------------------------------
// RSDK Project: Sonic 2
// Script Description: Checkered Ball Object
// Script Author: Christian Whitehead/Simon Thomley
// Unpacked by Rubberduckycooly's script unpacker
// ----------------------------------

// ========================
// Aliases
// ========================

private alias object.value0  : object.unused // reset a couple of times, never used for anything noteworthy
private alias object.value1  : object.roundedPos.x
private alias object.value2  : object.roundedPos.y
private alias object.value3  : object.ballAngle
private alias object.value4  : object.groundAngle
private alias object.value5  : object.angleVel
private alias object.value6  : object.playerControlled
private alias object.value7  : object.startPos.x
private alias object.value8  : object.startPos.y
// object.value9 is unused
private alias object.value10 : object.activePlayers
private alias object.value11 : object.badnikBonus
private alias object.value12 : object.collisionOffset.x
private alias object.value13 : object.collisionOffset.y
// object.value14 is unused
private alias object.value15 : object.parent // Should be a ballcannon entity

// States
private alias 0 : CHECKEREDBALL_NORMAL
private alias 1 : CHECKEREDBALL_LAUNCHED

// Player Aliases
private alias object.xpos : player.xpos
private alias object.ypos : player.ypos
private alias object.xvel : player.xvel
private alias object.speed : player.speed
private alias object.gravity : player.gravity
private alias object.pushing : player.pushing
private alias object.controlLock : player.controlLock
private alias object.collisionLeft : player.collisionLeft
private alias object.collisionRight : player.collisionRight
private alias object.left : player.left
private alias object.right : player.right

private alias object.value20 : player.topSpeed
private alias object.value21 : player.acceleration

// Super States
private alias 1 : SUPERSTATE_SUPER
private alias 2 : SUPERSTATE_FADEOUT

// Monitor Aliases
private alias object.value0 : monitor.contentsPos.y
private alias object.value1 : monitor.timer

// Spring Aliases
private alias object.value0 : spring.timer 
private alias object.value1 : spring.active
private alias object.value2 : spring.extraVelocity

// Plane Sw V Aliases
private alias object.value0 : planeSwV.extendTop
private alias object.value1 : planeSwV.extendBottom
private alias object.value2 : planeSwV.planeR
private alias object.value3 : planeSwV.planeL
private alias object.value4 : planeSwV.drawOrderR
private alias object.value5 : planeSwV.drawOrderL
private alias object.value6 : planeSwV.onGround

// Plane Sw H Aliases
private alias object.value0 : planeSwH.extendLeft
private alias object.value1 : planeSwH.extendRight
private alias object.value2 : planeSwH.planeR
private alias object.value3 : planeSwH.planeL
private alias object.value4 : planeSwH.drawOrderR
private alias object.value5 : planeSwH.drawOrderL
private alias object.value6 : planeSwH.onGround

// AnimalPrison Aliases
private alias object.value2 : animalPrison.buttonPos
private alias object.value3 : animalPrison.lockPos.x
private alias object.value4 : animalPrison.lockPos.y

// AnimalPrison States
private alias 0 : ANIMALPRISON_AWAITOPEN
private alias 1 : ANIMALPRISON_OPENING

// CFloor Aliases
private alias 0 : CFLOOR_ACTIVE
private alias 1 : CFLOOR_COLLAPSE
private alias 3 : CFLOOR_NONE

// SwingPlatform
private alias object.value1 : swingPlatform.drawPos.x
private alias object.value2 : swingPlatform.drawPos.y
private alias object.value3 : swingPlatform.collisionOffset.x
private alias object.value4 : swingPlatform.collisionOffset.y

// Tile Flag Aliases
private alias 0 : TILE_NORMAL
private alias 1 : TILE_OILPOOL
private alias 3 : TILE_OILSTRIP
private alias 5 : TILE_OILSLIDE

// Tile Info ID Aliases
private alias 8 : TILEINFO_ANGLEB

// VPushSpring Aliases
private alias object.value1 : vPushSpring.pushOffset
private alias object.value2 : vPushSpring.collisionOffset.y
private alias object.value3 : vPushSpring.beingPushed

// VPushSpring States
private alias 2 : VPUSHSPRING_RELEASE

// HPushSpring Aliases
private alias object.value1 : hPushSpring.pushOffset
private alias object.value2 : hPushSpring.beingPushed

// HPushSpring States
private alias 1 : HPUSHSPRING_PUSHING

// Elevator Aliases
private alias object.value4 : elevator.collisionOffset.y

// BallCannon Aliases
private alias object.value0 : ballCannon.timer
private alias object.value1 : ballCannon.tablePos
private alias object.value5 : ballCannon.activePlayers
private alias object.value6 : ballCannon.lastCannon

// BallCannon States
private alias 1 : BALLCANNON_ENTERED
private alias 4 : BALLCANNON_EXITED

// PushButton Aliases
private alias object.value0 : pushButton.pressed
private alias object.value1 : pushButton.stood

// GasBreakBlock States
private alias 0 : GASBREAKBLOCK_BLOCKS
private alias 1 : GASBREAKBLOCK_LAUNCHER
private alias 2 : GASBREAKBLOCK_DEBRIS

// GasPlatform Aliases
private alias object.value1 : gasPlatform.centerPos.y
private alias object.value2 : gasPlatform.collisionOffset.y

// OOZEggman Aliases
private alias object.value0 : oozEggman.timer
private alias object.value3 : oozEggman.health
private alias object.value4 : oozEggman.invincibilityTimer
private alias object.value5 : oozEggman.exploding
private alias object.value9 : oozEggman.spawnHarpoon

// OOZEggman States
private alias 7 : OOZEGGMAN_EXPLODE

// OOZEggman Animations
private alias 3 : OOZEGGANI_HIT
private alias 4 : OOZEGGANI_DEFEATED


// ========================
// Function Declarations
// ========================

reserve function CheckeredBall_DebugDraw
reserve function CheckeredBall_DebugSpawn
reserve function CheckeredBall_HandlePlayerMove
reserve function CheckeredBall_PlayerInteraction
reserve function CheckeredBall_BadnikBreak
reserve function CheckeredBall_ReflectProjectile
reserve function CheckeredBall_SpringBounce
reserve function CheckeredBall_CLedgeCollapse
reserve function CheckeredBall_HandlePhysics


// ========================
// Tables
// ========================

private table CheckeredBall_heightArray
	0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20
end table

// Seems to be unused, and as such, its purpose is unknown...
// This same table exists in S1's and Mania's unused CheckeredBall objects as well, for whatever reason
private table CheckeredBall_unusedTable
	253, 251, 248, 245, 243, 240, 237, 235, 232, 229, 226, 224, 221, 219, 216, 213, 210, 208, 205, 203, 200, 197, 195, 192
end table


// ========================
// Function Definitions
// ========================

private function CheckeredBall_DebugDraw
	DrawSprite(1)
end function


private function CheckeredBall_DebugSpawn
	CreateTempObject(TypeName[Checkered Ball], 0, object.xpos, object.ypos)
	object[tempObjectPos].gravity = GRAVITY_AIR
	object[tempObjectPos].startPos.x = object.xpos
	object[tempObjectPos].startPos.y = object.ypos
	object[tempObjectPos].roundedPos.x = object.xpos
	object[tempObjectPos].roundedPos.y = object.ypos
end function


private function CheckeredBall_HandlePlayerMove
	object.activePlayers = 0

	temp2 = 0
	foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
		CheckEqual(options.attractMode, true)
		temp1 = checkResult
		CheckEqual(currentPlayer, 1)
		temp1 &= checkResult
		temp0 = player[currentPlayer].acceleration
		temp0 >>= 1

		if player[currentPlayer].gravity == 0
			if player[currentPlayer].xvel < 0
				BoxCollisionTest(C_TOUCH, object.entityPos, 0, -14, 15, 14, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
				if checkResult == true
					if player[currentPlayer].left == true
						if temp1 == false
							SetBit(object.activePlayers, temp2, true)
							temp1 = player[currentPlayer].topSpeed
							FlipSign(temp1)
							if object.speed > temp1
								object.speed -= temp0
							end if
							object.playerControlled = true
						end if

						player[currentPlayer].xpos = player[currentPlayer].collisionRight
						player[currentPlayer].xpos += 14
						player[currentPlayer].xpos <<= 16
						player[currentPlayer].xpos += object.roundedPos.x

						if object.xvel < 0
							if player[currentPlayer].speed < object.speed
								player[currentPlayer].xvel = object.xvel
								player[currentPlayer].speed = object.speed
							end if
						end if

						player[currentPlayer].pushing = 2
					end if
				end if
			else
				if player[currentPlayer].xvel > 0
					BoxCollisionTest(C_TOUCH, object.entityPos, -15, -14, 0, 14, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
					if checkResult == true
						if player[currentPlayer].right == true
							if temp1 == false
								SetBit(object.activePlayers, temp2, true)
								if object.speed < player[currentPlayer].topSpeed
									object.speed += temp0
								end if
								object.playerControlled = true
							end if

							player[currentPlayer].xpos = player[currentPlayer].collisionLeft
							player[currentPlayer].xpos += -14
							player[currentPlayer].xpos <<= 16
							player[currentPlayer].xpos += object.roundedPos.x

							if object.xvel > 0
								if player[currentPlayer].speed > object.speed
									player[currentPlayer].xvel = object.xvel
									player[currentPlayer].speed = object.speed
								end if
							end if

							player[currentPlayer].pushing = 2
						end if
					end if
				end if
			end if
		end if
		temp2++
	next
end function


private function CheckeredBall_PlayerInteraction
	temp6 = object.xpos
	temp6 &= 0xFFFF0000
	temp6 -= object.roundedPos.x

	temp7 = object.ypos
	temp7 &= 0xFFFF0000
	temp7 -= object.roundedPos.y

	temp4 = object.xpos
	temp5 = object.ypos
	object.xpos = object.roundedPos.x
	object.ypos = object.roundedPos.y

	temp2 = 0
	foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
		BoxCollisionTest(C_TOUCH, object.entityPos, -2, -2, 2, 2, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
		if checkResult == false
			GetBit(temp0, object.activePlayers, temp2)
			if temp0 == true
				if player[currentPlayer].xpos < object.xpos
					temp1 = player[currentPlayer].collisionRight
					FlipSign(temp1)
					temp1 -= 14
				else
					temp1 = player[currentPlayer].collisionLeft
					FlipSign(temp1)
					temp1 += 14
				end if
				
				temp1 <<= 16
				player[currentPlayer].xpos = temp1
				player[currentPlayer].xpos += temp4
			else
				BoxCollisionTest(C_SOLID, object.entityPos, -14, -14, 14, 14, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
				if checkResult == COL_TOP
					player[currentPlayer].xpos += temp6
					temp0 = temp6
					temp0 >>= 1
					player[currentPlayer].xpos += temp0
					player[currentPlayer].ypos += temp7

					if player[currentPlayer].xvel != 0
						temp0 = player[currentPlayer].acceleration
						temp0 >>= 2
						if player[currentPlayer].xvel > 0
							FlipSign(temp0)
						end if
					else
						temp0 = 0
					end if

					object.speed += temp0
					if temp0 != 0
						object.playerControlled = true
					end if

					object.xpos = temp4
					object.ypos = temp5
					BoxCollisionTest(C_SOLID, object.entityPos, -14, -14, 14, 14, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
					object.xpos = object.roundedPos.x
					object.ypos = object.roundedPos.y
					if checkResult == COL_NONE
						temp0 = temp6
						temp1 = temp6
						temp0 >>= 1
						temp1 += temp0
						player[currentPlayer].speed += temp0
						player[currentPlayer].xvel = player[currentPlayer].speed
						player[currentPlayer].gravity = GRAVITY_AIR
						player[currentPlayer].controlLock = 15
					end if
				end if

				if checkResult == COL_BOTTOM
					if object.yvel >= 0
						if player[currentPlayer].gravity == 0
							CallFunction(Player_Kill)
						end if
					end if
				end if
			end if

			temp2++
		end if
	next

	object.xpos = temp4
	object.ypos = temp5
end function


private function CheckeredBall_BadnikBreak
	temp0 = object.xvel
	temp0 |= object.yvel
	if temp0 != 0
		ResetObjectEntity(object[arrayPos0].entityPos, TypeName[Blank Object], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		Rand(checkResult, 32)
		if checkResult >= 16
			CreateTempObject(animalType1, 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		else
			CreateTempObject(animalType2, 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		end if
		object[tempObjectPos].priority = PRIORITY_ACTIVE_SMALL
		
		CreateTempObject(TypeName[Smoke Puff], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
		object[tempObjectPos].drawOrder = 4

		CreateTempObject(TypeName[Object Score], object.badnikBonus, object[arrayPos0].xpos, object[arrayPos0].ypos)
		object[tempObjectPos].drawOrder = 4

		PlaySfx(SfxName[Destroy], false)

		// No 2P trickery here!
		switch object.badnikBonus
		case 0
			player.score += 100
			break

		case 1
			player.score += 200
			break

		case 2
			player.score += 500
			break

		case 3
		case 4
		case 5
		case 6
		case 7
		case 8
		case 9
		case 10
		case 11
		case 12
		case 13
		case 14
			player.score += 1000
			break

		case 15
			player.score += 10000
			break
			
		end switch

		if object.badnikBonus < 15
			object.badnikBonus++
		end if
	end if
end function


private function CheckeredBall_ReflectProjectile
	// Do some maths to find where the projectile should go now
	temp0 = object.xpos
	temp0 -= object[arrayPos0].xpos
	temp1 = object.ypos
	temp1 -= object[arrayPos0].ypos

	ATan2(temp2, temp0, temp1)
	Sin256(temp0, temp2)
	Cos256(temp1, temp2)

	object[arrayPos0].xvel = temp1
	object[arrayPos0].xvel *= -0x800
	object[arrayPos0].yvel = temp0
	object[arrayPos0].yvel *= -0x800
end function


private function CheckeredBall_SpringBounce
	switch object[arrayPos0].propertyValue
	case 0
		temp0 = spring[arrayPos0].active
		if object.gravity == 1
			temp0 = true
		end if

		if object.collisionMode > 0
			if object.yvel < 0
				temp0 = true
			end if
		end if

		if temp0 == false
			BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -14, -14, 14, 14)
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, -10, 14, -6, object.entityPos, -14, -14, 14, 14)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.gravity = GRAVITY_AIR
				object.speed = object.xvel
				object.yvel = temp7
				FlipSign(object.yvel)
				object.yvel += spring[arrayPos0].extraVelocity
				PlaySfx(SfxName[Spring], false)
			end if
		else
			if object.yvel >= 0
				BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -14, -14, 14, 14)
				BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, -10, 14, -6, object.entityPos, -14, -14, 14, 14)
				if checkResult == true
					spring[arrayPos0].timer = 1
					object.tileCollisions = true
					object.gravity = GRAVITY_AIR
					object.speed = object.xvel
					object.yvel = temp7
					FlipSign(object.yvel)
					object.yvel += spring[arrayPos0].extraVelocity
					PlaySfx(SfxName[Spring], false)
				end if
			end if
		end if
		break

	case 1
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -8, -14, 8, 14, object.entityPos, -14, -14, 14, 14)
		if object.gravity == 0
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, 6, -14, 10, 14, object.entityPos, -14, -14, 14, 14)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.speed = temp7
				object.collisionMode = CMODE_FLOOR
				object.direction = FLIP_NONE
				PlaySfx(SfxName[Spring], false)
			end if
		end if
		break

	case 2
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -8, -14, 8, 14, object.entityPos, -14, -14, 14, 14)
		if object.gravity == 0
			BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -10, -14, -6, 14, object.entityPos, -14, -14, 14, 14)
			if checkResult == true
				spring[arrayPos0].timer = 1
				object.tileCollisions = true
				object.speed = temp7
				FlipSign(object.speed)
				object.collisionMode = CMODE_FLOOR
				object.direction = FLIP_X
				PlaySfx(SfxName[Spring], false)
			end if
		end if
		break

	case 3
		BoxCollisionTest(C_SOLID, object[arrayPos0].entityPos, -14, -8, 14, 8, object.entityPos, -14, -14, 14, 14)
		BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -14, 6, 14, 10, object.entityPos, -14, -14, 14, 14)
		if checkResult == true
			spring[arrayPos0].timer = 1
			if object.collisionMode == 2
				FlipSign(object.speed)
				FlipSign(object.xvel)
			end if
			object.tileCollisions = true
			object.gravity = GRAVITY_AIR
			object.speed = object.xvel
			object.yvel = temp7
			PlaySfx(SfxName[Spring], false)
		end if
		break
		
	end switch
end function


private function CheckeredBall_CLedgeCollapse
	if object[arrayPos0].state < CFLOOR_NONE
	
		// what? why is this here??
		// If I had to guess, I'd say it's just a debugging leftover or something, but it's certainly very strange
		player.score++

		if object[arrayPos0].propertyValue < 2
			if object.yvel >= 0
				BoxCollisionTest(C_PLATFORM, arrayPos0, -64, -24, 64, 0, object.entityPos, -10, -14, 10, 14)
			else
				checkResult = COL_NONE
			end if
		else
			BoxCollisionTest(C_SOLID, arrayPos0, -64, -16, 64, 16, object.entityPos, -10, -14, 10, 14)
		end if

		if checkResult == COL_TOP
			if object[arrayPos0].state == CFLOOR_ACTIVE
				object[arrayPos0].state = CFLOOR_COLLAPSE
				object[arrayPos0].priority = PRIORITY_ACTIVE
			end if
		end if
	end if
end function


private function CheckeredBall_HandlePhysics
	if object.gravity == 1
		object.yvel += 0x3800
		object.speed = object.xvel
	else
		Sin256(temp0, object.groundAngle)
		temp0 *= 0x2000
		temp0 >>= 8
		object.speed += temp0
		object.angleVel = object.speed
		if temp0 == 0
			if object.playerControlled == false
				if object.speed > 0
					object.speed -= 0x400
					if object.speed < 0
						object.speed = 0
					end if
				else
					object.speed += 0x400
					if object.speed > 0
						object.speed = 0
					end if
				end if
			end if
		end if

		Cos256(temp0, object.groundAngle)
		temp0 *= object.speed
		temp0 >>= 8
		object.xvel = temp0

		Sin256(temp0, object.groundAngle)
		temp0 *= object.speed
		temp0 >>= 8
		object.yvel = temp0
	end if

	object.playerControlled = false
end function


// ========================
// Events
// ========================

event ObjectUpdate
	if object.state == CHECKEREDBALL_NORMAL
		object.priority = PRIORITY_ACTIVE
		CallFunction(CheckeredBall_HandlePlayerMove)
		object.ballAngle += object.angleVel
		object.collisionLeft = -14
		object.collisionTop = -14
		object.collisionRight = 14
		object.collisionBottom = 14
		CallFunction(CheckeredBall_HandlePhysics)
		ProcessObjectMovement()
		object.groundAngle = object.angle
	else
		object.collisionLeft = -14
		object.collisionTop = -14
		object.collisionRight = 14
		object.collisionBottom = 14
		ProcessObjectMovement()
		object.ballAngle += 0x40000
	end if

	// Cycle through all objects
	foreach (0, arrayPos0, ACTIVE_ENTITIES)
		// Jump to the object's type
		// If not listed here, it has no interaction
		switch object[arrayPos0].type
		case TypeName[Octus]
			BoxCollisionTest(C_TOUCH, object.entityPos, -14, -14, 14, 14, arrayPos0, -16, -12, 16, 12)
			if checkResult == true
				CallFunction(CheckeredBall_BadnikBreak)
			end if
			break

		case TypeName[Aquis]
			BoxCollisionTest(C_TOUCH, object.entityPos, -14, -14, 14, 14, arrayPos0, -16, -10, 16, 10)
			if checkResult == true
				CallFunction(CheckeredBall_BadnikBreak)
			end if
			break

		case TypeName[Octus Shot]
			BoxCollisionTest(C_TOUCH, object.entityPos, -14, -14, 14, 14, arrayPos0, -4, -4, 4, 4)
			if checkResult == true
				CallFunction(CheckeredBall_ReflectProjectile)
			end if
			break

		case TypeName[Aquis Shot]
			BoxCollisionTest(C_TOUCH, object.entityPos, -14, -14, 14, 14, arrayPos0, -4, -4, 4, 4)
			if checkResult == true
				CallFunction(CheckeredBall_ReflectProjectile)
			end if
			break

		case TypeName[Monitor]
			BoxCollisionTest(C_TOUCH, object.entityPos, -14, -14, 14, 14, arrayPos0, -16, -14, 16, 16)
			if checkResult == true
				object[arrayPos0].state = 0
				CreateTempObject(TypeName[Smoke Puff], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
				object[tempObjectPos].drawOrder = 4
				object[arrayPos0].type = TypeName[Broken Monitor]

				if object[arrayPos0].priority != PRIORITY_XBOUNDS_DESTROY
					object[arrayPos0].priority = PRIORITY_ACTIVE
				end if

				object[arrayPos0].alpha = 0xFF
				monitor[arrayPos0].contentsPos.y = object[arrayPos0].ypos
				monitor[arrayPos0].timer = -0x30000
				PlaySfx(SfxName[Destroy], false)
			end if
			break

		case TypeName[Spikes]
			switch object[arrayPos0].propertyValue
			case 0
				BoxCollisionTest(C_SOLID, arrayPos0, -16, -16, 16, 16, object.entityPos, -14, -14, 14, 14)
				break
				
			case 1
				BoxCollisionTest(C_SOLID, arrayPos0, -16, -16, 15, 16, object.entityPos, -14, -14, 14, 14)
				break
				
			case 2
				BoxCollisionTest(C_SOLID, arrayPos0, -15, -16, 16, 16, object.entityPos, -14, -14, 14, 14)
				break
				
			case 3
				BoxCollisionTest(C_SOLID, arrayPos0, -16, -16, 16, 15, object.entityPos, -14, -14, 14, 14)
				break
				
			end switch
			
			switch checkResult
			case COL_TOP
				object.gravity = GRAVITY_GROUND
				break

			case COL_LEFT
				if object.speed > 0
					object.speed = 0
				end if
				break

			case COL_RIGHT
				if object.speed < 0
					object.speed = 0
				end if
				break
			end switch
			break

		case TypeName[Yellow Spring]
			temp7 = 0xA0000
			CallFunction(CheckeredBall_SpringBounce)
			break

		case TypeName[Red Spring]
			temp7 = 0x100000
			CallFunction(CheckeredBall_SpringBounce)
			break

		case TypeName[V Push Spring]
			temp0 = vPushSpring[arrayPos0].pushOffset
			temp0 -= vPushSpring[arrayPos0].collisionOffset.y
			temp0 >>= 16
			temp0 -= 20
			BoxCollisionTest(C_SOLID, arrayPos0, -16, temp0, 16, 20, object.entityPos, -10, -14, 10, 14)
			if checkResult == COL_TOP
				vPushSpring[arrayPos0].beingPushed |= true
				object.ypos += vPushSpring[arrayPos0].collisionOffset.y
				if object[arrayPos0].state == VPUSHSPRING_RELEASE
					if vPushSpring[arrayPos0].pushOffset == 0x120000
						object.gravity = GRAVITY_AIR
						object.yvel = -0x60000
						object.unused = 0
						PlaySfx(SfxName[Spring], false)
					end if
				end if
			end if
			break

		case TypeName[H Push Spring]
			if object[arrayPos0].direction == FLIP_NONE
				temp0 = -20
				temp1 = hPushSpring[arrayPos0].pushOffset
				temp1 >>= 16
				FlipSign(temp1)
				temp1 += 22
			else
				temp0 = hPushSpring[arrayPos0].pushOffset
				temp0 >>= 16
				temp0 -= 22
				temp1 = 20
			end if

			temp2 = object.speed
			BoxCollisionTest(C_SOLID, arrayPos0, temp0, -16, temp1, 16, object.entityPos, -10, -14, 10, 14)
			object.speed = temp2
			switch checkResult
			case COL_LEFT
				temp0 >>= 8
				object.speed -= temp0
				if object.speed > 0
					hPushSpring[arrayPos0].beingPushed |= true
				end if

				if object[arrayPos0].state == HPUSHSPRING_PUSHING
					if hPushSpring[arrayPos0].pushOffset > 0x10000
						object.speed = -12
						object.speed *= hPushSpring[arrayPos0].pushOffset
						object.speed /= 18
						object.collisionMode = CMODE_FLOOR
						PlaySfx(SfxName[Spring], false)
					end if
				end if
				break

			case COL_RIGHT
				temp0 >>= 5
				object.speed -= temp0
				if object.speed < 0
					hPushSpring[arrayPos0].beingPushed |= true
				end if

				if object[arrayPos0].state == HPUSHSPRING_PUSHING
					if hPushSpring[arrayPos0].pushOffset > 0x10000
						object.speed = 12
						object.speed *= hPushSpring[arrayPos0].pushOffset
						object.speed /= 18
						PlaySfx(SfxName[Spring], false)
					end if
				end if
				break
			end switch
			break

		case TypeName[Ball Cannon]
			BoxCollisionTest(C_TOUCH, arrayPos0, -4, -4, 4, 4, object.entityPos, -10, -14, 10, 14)
			if checkResult == true
				temp0 = 1
				temp0 <<= 5
				temp0 &= object[arrayPos0].activePlayers
				if temp0 == false
					object.state = CHECKEREDBALL_LAUNCHED
					object.xpos = object[arrayPos0].xpos
					object.ypos = object[arrayPos0].ypos
					object.speed = 0
					object.xvel = 0
					object.yvel = 0
					object.visible = false
					object.tileCollisions = false
					temp0 = 1
					temp0 <<= 5
					ballCannon[arrayPos0].activePlayers |= temp0
					ballCannon[arrayPos0].timer = 0
					ballCannon[arrayPos0].tablePos = 0
					object[arrayPos0].state = BALLCANNON_ENTERED
					if object[arrayPos0].priority != PRIORITY_XBOUNDS_DESTROY
						object[arrayPos0].priority = PRIORITY_ACTIVE
					end if
					object.parent = arrayPos0
					PlaySfx(SfxName[Rolling], false)
				end if
			end if
			break

		case TypeName[Gas Platform]
			temp0 = object[arrayPos0].ypos

			object[arrayPos0].ypos = gasPlatform[arrayPos0].centerPos.y
			BoxCollisionTest(C_SOLID, arrayPos0, -32, 8, 32, 48, object.entityPos, -10, -14, 10, 14)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, 0, 16, 24, object.entityPos, -10, -14, 10, 14)

			object[arrayPos0].ypos = temp0
			object[arrayPos0].ypos -= gasPlatform[arrayPos0].collisionOffset.y
			BoxCollisionTest(C_PLATFORM, arrayPos0, -24, -8, 24, 12, object.entityPos, -10, -14, 10, 14)
			if checkResult == true
				object.collisionOffset.y = gasPlatform[arrayPos0].collisionOffset.y
			end if

			object[arrayPos0].ypos += gasPlatform[arrayPos0].collisionOffset.y
			break

		case TypeName[Elevator]
			object[arrayPos0].ypos -= elevator[arrayPos0].collisionOffset.y

			if object[arrayPos0].frame == 0
				BoxCollisionTest(C_PLATFORM, arrayPos0, -64, -12, 64, 12, object.entityPos, -10, -14, 10, 14)
			else
				BoxCollisionTest(C_PLATFORM, arrayPos0, -32, -12, 32, 12, object.entityPos, -10, -14, 10, 14)
			end if

			if checkResult == true
				object.collisionOffset.y = elevator[arrayPos0].collisionOffset.y
			end if

			object[arrayPos0].ypos += elevator[arrayPos0].collisionOffset.y
			break

		case TypeName[Swing Platform]
			temp0 = object[arrayPos0].xpos
			temp1 = object[arrayPos0].ypos
			object[arrayPos0].xpos = swingPlatform[arrayPos0].drawPos.x
			object[arrayPos0].ypos = swingPlatform[arrayPos0].drawPos.y
			object[arrayPos0].xpos -= swingPlatform[arrayPos0].collisionOffset.x
			object[arrayPos0].ypos -= swingPlatform[arrayPos0].collisionOffset.y
			BoxCollisionTest(C_PLATFORM, object[arrayPos0].entityPos, -32, -8, 32, 8, object.entityPos, -10, -14, 10, 14)
			if checkResult == true
				object.collisionOffset.x = swingPlatform[arrayPos0].collisionOffset.x
				object.collisionOffset.y = swingPlatform[arrayPos0].collisionOffset.y
			end if
			object[arrayPos0].xpos = temp0
			object[arrayPos0].ypos = temp1
			break

		case TypeName[C Floor]
			CallFunction(CheckeredBall_CLedgeCollapse)
			break

		case TypeName[Plane Sw V]
			// Why is this here, plane switches don't even exist in OOZ...
			// (Likely just leftover from copying S1's CheckeredBall object, since there *are* path swappers in GHZ)
			CheckEqual(planeSwV[arrayPos0].onGround, false)
			temp0 = checkResult
			CheckEqual(object.gravity, 0)
			temp0 |= checkResult
			if temp0 == true
				BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, -12, planeSwV[arrayPos0].extendTop, 12, planeSwV[arrayPos0].extendBottom, object.entityPos, -14, -2, 14, 2)
				if checkResult == true
					if object.xvel > 0
						object.collisionPlane = planeSwV[arrayPos0].planeL
						object.drawOrder = planeSwV[arrayPos0].drawOrderL
					else
						object.collisionPlane = planeSwV[arrayPos0].planeR
						object.drawOrder = planeSwV[arrayPos0].drawOrderR
					end if
				end if
			end if
			break

		case TypeName[Plane Sw H]
			// See above note, likely the same story here
			// Although the Origins version of the S1 GHZ Checkered Ball script broke Sw H interactions, this was left alone and still works just fine
			CheckEqual(planeSwH[arrayPos0].onGround, false)
			temp0 = checkResult
			CheckEqual(object.gravity, 0)
			temp0 |= checkResult
			if temp0 == true
				BoxCollisionTest(C_TOUCH, object[arrayPos0].entityPos, planeSwH[arrayPos0].extendLeft, -12, planeSwH[arrayPos0].extendRight, 12, object.entityPos, -14, -2, 14, 2)
				if checkResult == true
					if object.yvel > 0
						object.collisionPlane = planeSwH[arrayPos0].planeL
						object.drawOrder = planeSwH[arrayPos0].drawOrderL
					else
						object.collisionPlane = planeSwH[arrayPos0].planeL
						object.drawOrder = planeSwH[arrayPos0].drawOrderR
					end if
				end if
			end if
			break

		case TypeName[Eggman]
			if oozEggman[arrayPos0].health != 0
				if object[arrayPos0].visible == true
					if oozEggman[arrayPos0].invincibilityTimer == 0
						BoxCollisionTest(C_TOUCH, arrayPos0, -24, -10, 24, 24, object.entityPos, -14, -14, 14, 14)
						if checkResult == true
							oozEggman[arrayPos0].health--
							if oozEggman[arrayPos0].health == 0
								player.score += 1000
								object[arrayPos0].animationTimer = 0
								object[arrayPos0].animation = OOZEGGANI_DEFEATED
								oozEggman[arrayPos0].exploding = true
								oozEggman[arrayPos0].timer = 0
								object[arrayPos0].state = OOZEGGMAN_EXPLODE
								SetPaletteEntry(0, 192, 0x000000)
							else
								object[arrayPos0].animationTimer = 0
								object[arrayPos0].animation = OOZEGGANI_HIT
								oozEggman[arrayPos0].invincibilityTimer = 64
								oozEggman[arrayPos0].spawnHarpoon = true
								PlaySfx(SfxName[Boss Hit], false)
							end if
						end if
					end if
				end if
			end if
			break

		case TypeName[Push Button]
			if object[arrayPos0].frame == 0
				BoxCollisionTest(C_SOLID, arrayPos0, -14, -6, 14, 12, object.entityPos, -14, -14, 14, 14)
				if checkResult == COL_TOP
					pushButton[arrayPos0].stood 	= true
					pushButton[arrayPos0].pressed 	= true
					object.ypos += 40000
					PlaySfx(SfxName[Button Press], false)
				end if
			else
				if object.yvel >= 0
					BoxCollisionTest(C_PLATFORM, arrayPos0, -14, -6, 14, 12, object.entityPos, -14, -14, 14, 14)
					if checkResult == true
						pushButton[arrayPos0].stood = true
						object.ypos += 0x20000
					else
						BoxCollisionTest(C_TOUCH, arrayPos0, -20, -12, 20, 8, object.entityPos, -14, -14, 14, 14)
						if checkResult == true
							object.ypos = object.collisionBottom
							FlipSign(object.ypos)
							object.ypos <<= 16
							object.ypos += object[arrayPos0].ypos
							object.ypos -= 0x20000
							object.gravity = GRAVITY_AIR
						end if
					end if
				end if
			end if
			break

		case TypeName[Checkered Ball]
			if arrayPos0 != object.entityPos
				temp0 = object.xpos
				temp0 -= object[arrayPos0].xpos
				Abs(temp0)
				temp0 >>= 17
				if temp0 >= 16
					temp0 = 15
				end if
				GetTableValue(temp6, temp0, CheckeredBall_heightArray)
				temp0 = temp6
				temp0 >>= 1
				temp6 += temp0
				temp6 -= 14
				temp7 = temp6
				FlipSign(temp7)
				temp0 = object.xvel
				temp1 = object.yvel
				temp2 = object.speed
				BoxCollisionTest(C_SOLID, arrayPos0, -14, temp6, 14, temp7, object.entityPos, -14, -14, 14, 14)
				switch checkResult
				case COL_LEFT
				case COL_RIGHT
					CheckGreater(temp0, 0)
					temp3 = checkResult
					CheckLower(object.xpos, object[arrayPos0].xpos)
					temp3 &= checkResult
					CheckLower(temp0, 0)
					temp4 = checkResult
					CheckGreater(object.xpos, object[arrayPos0].xpos)
					temp4 &= checkResult
					temp3 |= temp4
					if temp3 != 0
						object.xvel = object[arrayPos0].xvel
						object.speed = object[arrayPos0].speed
						object[arrayPos0].xvel = temp0
						object[arrayPos0].speed = temp2
					end if
					break

				case COL_TOP
					if object.xpos < object[arrayPos0].xpos
						object.speed -= 0xC00
						object.xvel -= 0xC00
						object[arrayPos0].speed += 0xC00
						object[arrayPos0].xvel += 0xC00
						object[arrayPos0].angleVel += 0xC00
					else
						object.speed += 0xC00
						object.xvel += 0xC00
						object[arrayPos0].speed -= 0xC00
						object[arrayPos0].xvel -= 0xC00
						object[arrayPos0].angleVel -= 0xC00
					end if
					// [Fallthrough]
				case COL_BOTTOM
					CheckGreater(temp1, 0)
					temp3 = checkResult
					CheckLower(object.ypos, object[arrayPos0].ypos)
					temp3 &= checkResult
					CheckLower(temp1, 0)
					temp4 = checkResult
					CheckGreater(object.ypos, object[arrayPos0].ypos)
					temp4 &= checkResult
					temp3 |= temp4
					if temp3 != 0
						object.yvel = object[arrayPos0].yvel
						object[arrayPos0].yvel = temp1
					end if
					break
					
				end switch
				
				BoxCollisionTest(C_TOUCH, arrayPos0, -14, temp6, 14, temp7, object.entityPos, -14, -14, 14, 14)
				if checkResult == true
					if object.xpos < object[arrayPos0].xpos
						object.xpos -= 0x10000
					else
						object.xpos += 0x10000
					end if
					if object.ypos < object[arrayPos0].ypos
						object.ypos -= 0x10000
					else
						object.ypos += 0x10000
					end if
				end if
			end if
			break
			
		case TypeName[Gas Break Block]
			switch object[arrayPos0].state
			case GASBREAKBLOCK_BLOCKS
				temp0 = object.yvel
				BoxCollisionTest(C_SOLID, arrayPos0, -16, -16, 16, 16, object.entityPos, -14, -14, 14, 14)
				object.yvel = temp0
				if checkResult == COL_TOP
					object.gravity = GRAVITY_AIR
					temp0 = 0
					temp1 = 0
					while temp0 < 16
						CreateTempObject(TypeName[Gas Break Block], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
						GetTableValue(object[tempObjectPos].frame, temp0, GasBreakBlock_blockFrames)
						GetTableValue(temp2, temp1, GasBreakBlock_blockOffsets)
						GetTableValue(object[tempObjectPos].xvel, temp1, GasBreakBlock_blockVelocities)
						temp1++
						GetTableValue(temp3, temp1, GasBreakBlock_blockOffsets)
						GetTableValue(object[tempObjectPos].yvel, temp1, GasBreakBlock_blockVelocities)
						temp1++
						object[tempObjectPos].xpos += temp2
						object[tempObjectPos].ypos += temp3
						object[tempObjectPos].state 	= GASBREAKBLOCK_DEBRIS
						object[tempObjectPos].drawOrder = 5
						temp0++
					loop
					object[arrayPos0].state++
					if object[arrayPos0].priority != PRIORITY_XBOUNDS_DESTROY
						object[arrayPos0].priority = PRIORITY_ACTIVE
					end if
					PlaySfx(SfxName[Block Break], false)
				end if
				break
				
			case GASBREAKBLOCK_LAUNCHER
				if object.state == CHECKEREDBALL_NORMAL
					BoxCollisionTest(C_TOUCH, arrayPos0, -8, -4, 8, 4, object.entityPos, -10, -14, 10, 14)
					if checkResult == true
						object.state = CHECKEREDBALL_LAUNCHED
						object.xpos = object[arrayPos0].xpos
						object.ypos = object[arrayPos0].ypos
						if object[arrayPos0].propertyValue == 0
							object.xvel = 0
							object.yvel = -0x80000
						else
							object.xvel = 0x80000
							object.yvel = 0
						end if
						PlaySfx(SfxName[Rolling], false)
					end if
				end if
				break
			end switch // object[arrayPos0].state
			break
			
		case TypeName[Animal Prison]
			animalPrison[arrayPos0].buttonPos = object[arrayPos0].ypos
			animalPrison[arrayPos0].buttonPos -= 0x300000
			temp2 = animalPrison[arrayPos0].buttonPos
			temp2 += 0x80000
			BoxCollisionTest(C_SOLID, arrayPos0, -32, -24, 32, 32, object.entityPos, -10, -14, 10, 14)
			BoxCollisionTest(C_SOLID, arrayPos0, -16, -40, 16, -24, object.entityPos, -10, -14, 10, 14)
			if object[arrayPos0].state == ANIMALPRISON_AWAITOPEN
				if checkResult == COL_TOP
					object[arrayPos0].state = ANIMALPRISON_OPENING
					stage.timeEnabled = false
					if Player_superState == SUPERSTATE_SUPER
						Player_superState = SUPERSTATE_FADEOUT
					end if
					object[arrayPos0].xvel = 0x80000
					object[arrayPos0].yvel = -0x40000
					animalPrison[arrayPos0].lockPos.x = object[arrayPos0].xpos
					animalPrison[arrayPos0].lockPos.y = object[arrayPos0].ypos
					CreateTempObject(TypeName[Smoke Puff], 0, object[arrayPos0].xpos, object[arrayPos0].ypos)
					object[tempObjectPos].ypos -= 0x180000
					PlaySfx(SfxName[Destroy], false)
				end if
			end if
			
			BoxCollisionTest(C_TOUCH, arrayPos0, -15, -48, 15, -24, object.entityPos, -10, -14, 10, 14)
			if checkResult == true
				temp0 = object.collisionBottom
				temp0 <<= 16
				temp0 += object.ypos
				if temp0 > animalPrison[arrayPos0].buttonPos
					if temp0 <= temp2
						animalPrison[arrayPos0].buttonPos = temp0
					end if
				end if
			end if
			break
			
		end switch
	next
	
	if object.parent != 0
		arrayPos0 = object.parent
		if object[arrayPos0].state == BALLCANNON_EXITED
			if ballCannon[arrayPos0].lastCannon == true
				object.state = CHECKEREDBALL_NORMAL
				object.gravity = GRAVITY_AIR
				object.tileCollisions = true
			end if
			object.visible = true
			object.unused = 0
			object.speed = object[arrayPos0].xvel
			object.xvel = object[arrayPos0].xvel
			object.yvel = object[arrayPos0].yvel
		end if
	end if
	
	temp1 = object.xpos
	temp1 >>= 16
	temp2 = object.ypos
	temp2 >>= 16
	temp2 += 6
	Get16x16TileInfo(temp0, temp1, temp2, TILEINFO_ANGLEB)
	switch temp0
	default
	case TILE_NORMAL
		break

	case TILE_OILPOOL
		if object.yvel > -1
			object.gravity = GRAVITY_GROUND
			object.yvel = 0
			object.ypos += 0x10000
		end if
		temp0 = object.speed
		temp0 >>= 5
		object.speed -= temp0
		break

	case TILE_OILSTRIP
		break

	case TILE_OILSLIDE
		break
		
	end switch

	if object.state == CHECKEREDBALL_NORMAL
		CallFunction(CheckeredBall_PlayerInteraction)
		temp0 = object.collisionOffset.x
		temp0 |= object.collisionOffset.y
		if temp0 != 0
			foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
				BoxCollisionTest(C_TOUCH, object.entityPos, -2, -2, 2, 2, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
				if checkResult == false
					BoxCollisionTest(C_SOLID, object.entityPos, -14, -14, 14, 14, currentPlayer, C_BOX, C_BOX, C_BOX, C_BOX)
					if checkResult == COL_TOP
						player[currentPlayer].xpos += object.collisionOffset.x
						player[currentPlayer].ypos += object.collisionOffset.y
					end if
				end if
			next

			object.xpos += object.collisionOffset.x
			object.ypos += object.collisionOffset.y
			object.collisionOffset.x = 0
			object.collisionOffset.y = 0
		end if
	end if

	if object.outOfBounds == true
		temp0 = object.xpos
		temp1 = object.ypos
		object.xpos = object.startPos.x
		object.ypos = object.startPos.y
		if object.outOfBounds == true
			object.roundedPos.x = object.xpos
			object.roundedPos.y = object.ypos
			object.xvel = 0
			object.yvel = 0
			object.speed = 0
			object.badnikBonus = 0
			object.groundAngle = 0
			object.activePlayers = 0
			object.ballAngle = 0
			object.angleVel = 0
			object.playerControlled = false
			object.priority = PRIORITY_BOUNDS
			object.state = 0
		else
			object.xpos = temp0
			object.ypos = temp1
		end if
	end if

	object.roundedPos.x = object.xpos
	object.roundedPos.x &= 0xFFFF0000
	object.roundedPos.y = object.ypos
	object.roundedPos.y &= 0xFFFF0000
end event


event ObjectDraw
	// Draw the ball frame
	object.rotation = object.ballAngle
	object.rotation >>= 14
	temp0 = object.rotation
	temp0 += 8
	temp0 >>= 4
	temp0 &= 7
	temp0++
	DrawSprite(temp0)

	// And draw the shine ontop of it too
	object.inkEffect 	= 3
	object.alpha 		= 0xA0
	DrawSpriteFX(0, FX_INK, object.xpos, object.ypos)
end event


event ObjectStartup
	LoadSpriteSheet("OOZ/Objects.gif")
	
	// Checkered Ball Frames
	SpriteFrame(-16, -16, 32, 32, 396, 190)
	SpriteFrame(-16, -16, 32, 32, 429, 190)
	SpriteFrame(-16, -16, 32, 32, 273, 133)
	SpriteFrame(-16, -16, 32, 32, 322, 100)
	SpriteFrame(-16, -16, 32, 32, 289, 100)
	SpriteFrame(-16, -16, 32, 32, 256, 100)
	SpriteFrame(-16, -16, 32, 32, 256, 67)
	SpriteFrame(-16, -16, 32, 32, 256, 34)
	SpriteFrame(-16, -16, 32, 32, 256, 1)

	// Load the animation file... but why?
	// Nothing's ever really done with it...
	LoadAnimation("WreckingBallJr.ani")

	// Setup all Checkered Ball objects in the level
	// Do note though that, at least in the normal game, this never really does anything since there aren't any Checkered Ball objects placed in the level
	foreach (TypeName[Checkered Ball], arrayPos0, ALL_ENTITIES)
		object[arrayPos0].gravity = GRAVITY_AIR
		object[arrayPos0].startPos.x = object.xpos
		object[arrayPos0].startPos.y = object.ypos
	next

	// Add this object to the debug object list
	SetTableValue(TypeName[Checkered Ball], DebugMode_ObjCount, DebugMode_TypesTable)
	SetTableValue(CheckeredBall_DebugDraw, DebugMode_ObjCount, DebugMode_DrawTable)
	SetTableValue(CheckeredBall_DebugSpawn, DebugMode_ObjCount, DebugMode_SpawnTable)
	DebugMode_ObjCount++
end event


// ========================
// Editor Events
// ========================

event RSDKDraw
	DrawSprite(0)

	object.inkEffect = INK_ADD
	object.alpha = 0xA0
	DrawSpriteFX(1, FX_INK, object.xpos, object.ypos)
end event


event RSDKLoad
	LoadSpriteSheet("OOZ/Objects.gif")
	SpriteFrame(-16, -16, 32, 32, 429, 190)
	SpriteFrame(-16, -16, 32, 32, 396, 190)
	
	SetVariableAlias(ALIAS_VAR_PROPVAL, "unused")
end event
